package cn.CommonUtils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Map;

@Slf4j
public class SignatureJwtUtils {
    /**
     * 生成JWT
     * 2019年11月16日
     * @throws UnsupportedEncodingException
     */
    public static String  generateJWT(String secret , SignatureAlgorithm signatureAlgorithm , Long expirationMillisecond , Map headers , Map claims) {

        Long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        byte[] encodedKey = new byte[0];
        try {
            encodedKey = secret.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            log.error("生成JWT出错！");
        }

        SecretKey secretKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");


        /**
         * 生成JWT时，携带信息 claims 和 payload 二选一
         * 一种是map
         * 一种可以选择用JSON，或者序列化之后的对象
         * 使用claims之后会把 setId、setIssuer、setSubject .. 覆盖
         * 使用payload时，自己需要携带的任何信息都要用payload
         *
         * 推荐使用claims
         */

        JwtBuilder jwtBuilder = Jwts.builder()
                .signWith(signatureAlgorithm, secretKey);

        if (MapUtils.isNotEmpty(headers)) {
            jwtBuilder.setHeader(headers);
        }
        if (MapUtils.isNotEmpty(claims)) {
            jwtBuilder.setClaims(claims);
        }


        Date expiration = new Date(nowMillis + expirationMillisecond);

        jwtBuilder.setExpiration(expiration);

        log.info(jwtBuilder.compact());
        log.info(jwtBuilder.toString());

        return jwtBuilder.compact();
    }
    /**
     * 解析JWT
     * 2019年11月16日
     * @throws UnsupportedEncodingException
     */
    public static Claims translateJWT(String jwt , String secret , SignatureAlgorithm signatureAlgorithm ,Long expirationMillisecond )  {

        Long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        byte[] encodedKey = new byte[0];
        try {
            encodedKey = secret.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            log.error("解析JWT时，encode密钥时出错！");
        }

        SecretKey secretKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");


        try {
            String signature = Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(jwt)
                    .getSignature();

            io.jsonwebtoken.Header header = Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(jwt)
                    .getHeader();

            Claims claims = Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(jwt)
                    .getBody();


            log.info("############## signature ##########################################################");
            log.info(signature);

            log.info("################# header #################################################");
            header.keySet().forEach(k -> {
                log.info(k + "：" + header.get(k).toString());
            });

            log.info("############ claims ############################################################");

            claims.keySet().forEach(k -> {
                log.info(k + "：" + claims.get(k).toString());
            });

            log.info("########################## expiration #######################################");
            log.info(claims.getExpiration().toLocaleString());
            log.info("currentTimeMillis : " + System.currentTimeMillis());
            log.info("JWT  expiration TimeMillis : " + claims.getExpiration().getTime());
            if (System.currentTimeMillis() > claims.getExpiration().getTime()) {
                log.warn("JWT expirationTime had been over !");
            } else {
                return claims;
            }

        } catch (io.jsonwebtoken.ExpiredJwtException eje) {
            eje.getStackTrace();
            log.error(eje.getLocalizedMessage());
            log.info("############## signature ##########################################################");
//            log.info(signature);

            log.info("################# header #################################################");
            eje.getHeader().keySet().forEach(k -> {
                log.info(k + "：" + eje.getHeader().get(k).toString());
            });

            log.info("############ claims ############################################################");

            eje.getClaims().keySet().forEach(k -> {
                log.info(k + "：" + eje.getClaims().get(k).toString());
            });
        } catch (io.jsonwebtoken.MalformedJwtException mje) {
            mje.getStackTrace();
            log.error(mje.getLocalizedMessage());
        }

        return null;

    }
}
